<?php

use app\library\DataReturn;
use Ramsey\Uuid\Codec\TimestampFirstCombCodec;
use Ramsey\Uuid\Generator\CombGenerator;
use Ramsey\Uuid\UuidFactory;
use think\Exception;
use think\facade\Log;
use think\response\Json;

// 应用公共文件
if (!function_exists('makeException')) {
    /**
     * 手动抛异常
     * @param string $message
     * @param int $code
     * @param bool $writeLog
     * @return mixed
     * @throws Exception
     */
    function makeException(string $message, int $code = -1, bool $writeLog = true)
    {
        if ($writeLog) {
            Log::write($message, \think\Log::ERROR);
        }
        throw new Exception($message, $code);
    }
}

if (!function_exists('dataReturn')) {

    /**
     * Service|模型内 数据统一返回
     * @param array|object|null $data
     * @param string $msg
     * @param int $code
     * @return DataReturn
     */
    function dataReturn($data = null, string $msg = 'success', int $code = 0): DataReturn
    {
        return new DataReturn($code, $data, $msg);
    }
}

if (!function_exists('dataErrorReturn')) {

    /**
     * Service|模型内 错误统一返回
     * @param string $msg
     * @param int $code
     * @param array|object|null $data
     * @return DataReturn
     */
    function dataErrorReturn(string $msg, int $code = -1, $data = null): DataReturn
    {
        Log::write($msg, \think\Log::ERROR);// 记录日志
        return new DataReturn($code, $data, $msg);
    }
}

if (!function_exists('jsonReturn')) {

    /**
     * 统一返回json数据
     * @param array|object|null $data
     * @param string $msg
     * @param int $code
     * @return Json
     */
    function jsonReturn($data = null, string $msg = 'success', int $code = 0): Json
    {
        return json(['code' => $code, 'data' => $data, 'msg' => $msg]);
    }
}

if (!function_exists('pageReturn')) {

    /**
     * 统一分页返回
     * @param array $list 分页数据
     * @return array
     */
    function pageReturn(array $list): array
    {
        if (0 == $list['code']) {
            return ['code' => 0, 'msg' => 'success', 'data' => $list['data']];
        }
        return ['code' => 0, 'msg' => 'success', 'data' => [
            'total' => 0,
            'rows' => [],
            'per_page' => 0,
            'current_page' => 1,
            'last_page' => 0,
        ]
        ];
    }
}

/**
 * 生成唯一id
 * @return string
 */
function uuid(): string
{
    return uniqid(md5(mt_rand()), true);
}

/**
 * 通过 TimestampFirstCombCodec
 * @link https://uuid.ramsey.dev/en/latest/customize/timestamp-first-comb-codec.html
 * @return string
 */
function uuidByComb(): string
{
    $factory = new UuidFactory();
    $codec = new TimestampFirstCombCodec($factory->getUuidBuilder());
    $factory->setCodec($codec);

    $factory->setRandomGenerator(new CombGenerator(
        $factory->getRandomGenerator(),
        $factory->getNumberConverter()
    ));
    $timestampFirstComb = $factory->uuid4();
    return $timestampFirstComb->toString();
}

/**
 * 获取毫秒时间戳
 * @return float|int
 */
function getMillisecondTimestamp()
{
    // 获取当前时间戳（包含微秒）
    list($microseconds, $seconds) = explode(' ', microtime());

    // 将秒数和微秒数转换为毫秒时间戳
    return floor(($seconds * 1000) + ($microseconds * 1000));
}

/**
 * $timestamp（时间戳）和$monthsCount（连续月份数量）。
 * 函数根据这两个参数计算从所给时间戳所在月份开始，连续指定数量的月份的开始和结束日期。
 * @param int $timestamp
 * @param int $monthsCount
 * @return array
 * @throws \Exception
 */
function getMonthsRangeFromTimestamp(int $timestamp, int $monthsCount): array
{
    // 创建DateTime对象，表示给定的时间戳
    $startDate = (new DateTime())->setTimestamp($timestamp);
    // 设置为给定月份的第一天
    $startDate->modify('first day of this month');

    // 创建日期间隔对象，设置为1个月
    $interval = new DateInterval('P1M');
    // 为避免影响原始$startDate，我们将起点设置为给定月份并向后移动($monthsCount - 1)个月作为结束点
    $startPeriod = clone $startDate;
    $startPeriod->sub(new DateInterval("P" . ($monthsCount - 1) . "M"));

    // 创建日期周期对象，包含接下来的$monthsCount个月
    $period = new DatePeriod($startPeriod, $interval, $monthsCount - 2);

    // 初始化数组来保存结果
    $months = [];

    foreach ($period as $dt) {
        // 获取每个月的第一天
        $firstDayOfMonth = $dt->format("Y-m-01");
        // 获取每个月的最后一天
        $lastDayOfMonth = $dt->format("Y-m-t");

        $months[] = [
            'start_date' => $firstDayOfMonth,
            'end_date' => $lastDayOfMonth,
        ];
    }

    // 确保包括初始月份在内的月份范围也被添加
    if ($monthsCount > 0) {
        $months[] = [
            'start_date' => $startDate->format("Y-m-01"),
            'end_date' => $startDate->format("Y-m-t")
        ];
    }

    return $months;
}

/**
 * 计算并返回从给定日期开始的$x$天之前的连续日期列表。
 *
 * @param string $baseDate 基准日期，格式为"Y-m-d"。
 * @param int $x 之前的天数。
 * @return array 连续日期列表。
 */
function getPreviousDates(string $baseDate, int $x): array
{
    // 将基准日期字符串转换为DateTime对象
    $endDate = DateTime::createFromFormat("Y-m-d", $baseDate);

    // 克隆结束日期并减去$x天来获取开始日期
    $startDate = (clone $endDate)->sub(new DateInterval("P{$x}D"));

    // 创建一个DatePeriod，每天迭代一次，直到包含基准日期
    $period = new DatePeriod($startDate, new DateInterval('P1D'), $endDate->modify('+1 day'));

    // 初始化日期列表数组
    $dateList = [];

    // 遍历每一天并添加到日期列表中
    foreach ($period as $date) {
        $dateList[] = $date->format("Y-m-d");
    }

    return $dateList;
}

/**
 * 计算并返回指定值在一维数组中出现的次数。
 *
 * 这个函数首先使用 array_count_values() 函数获取数组中每个唯一值的出现次数，
 * 然后根据给定的值来返回其在数组中出现的具体次数。如果该值在数组中不存在，
 * 则函数返回 0。
 *
 * @param array $array 要搜索的一维数组。
 * @param mixed $value 要计数的值。
 * @return int 指定值在数组中出现的次数。
 */
function countValueOccurrences($array, $value): int
{
    // 获取数组中所有值的计数
    $counts = array_count_values($array);

    // 如果指定的值存在于计数数组中，则返回其计数，否则返回0
    return $counts[$value] ?? 0;
}















